#pragma once
#include <list>
#include <mutex>
#include <condition_variable>
#include "db.hpp"
#include "online.hpp"
#include "room.hpp"
#include "util.hpp"

template<class T>
class match_queue
{
public:
    match_queue(){}
    ~match_queue(){}

    // 获取元素个数
    int size() 
    { 
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }
    // 判断是否为空
    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }
    // 等待：当队列玩家数量小于2时，被调用实现线程阻塞
    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);
    }
    // 入队数据并且唤醒线程：判断队列的数量是否大于等于2
    void push(const T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    // 出队数据
    bool pop(T& data) 
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (_list.empty()) {
            return false;
        }
        data = _list.front();
        _list.pop_front();
        return true;
    }
    // 移除指定的数据
    void remove(const T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.remove(data);
    }

private:
    std::list<T> _list; // 不直接使用queue是因为有中间删除数据的需要
    std::mutex _mutex;  // 实现list的线程安全
    std::condition_variable _cond; // 阻塞消费者线程
};

class matcher
{
public:
    matcher(room_manager* rm, user_table* ut, online_manager* om):
        _rm(rm), _ut(ut), _om(om),
        _th_normal(std::thread(&matcher::th_normal_entry, this)),
        _th_high(std::thread(&matcher::th_high_entry, this)),
        _th_super(std::thread(&matcher::th_super_entry, this)) {
        DLOG("游戏匹配模块初始化完毕");
    }
    ~matcher() {
        DLOG("游戏匹配模块销毁完毕");
    }

    // 添加用户到匹配队列中
    bool add(uint64_t uid)
    {
        // 1. 根据玩家的用户id，获取玩家的天梯分数
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false) 
        {
            DLOG("获取玩家：%ld 信息失败", uid);
            return false;
        }
        int score = user["score"].asInt();
        // 2. 添加到指定队列中
        if (score < 2000) {
            _q_normal.push(uid);
        } else if (score >= 2000 && score <= 3000) {
            _q_high.push(uid);
        } else {
            _q_super.push(uid);
        }
        return true;
    }
    // 删除匹配队列中的用户
    bool del(uint64_t uid)
    {
        // 1. 根据玩家的用户id，获取玩家的天梯分数
        Json::Value user;
        bool ret = _ut->select_by_id(uid, user);
        if (ret == false) 
        {
            DLOG("获取玩家：%ld 信息失败", uid);
            return false;
        }
        int score = user["score"].asInt();
        // 2. 添加用户
        if (score < 2000) {
            _q_normal.remove(uid);
        } else if (score >= 2000 && score <= 3000) {
            _q_high.remove(uid);
        } else {
            _q_super.remove(uid);
        }
        return true;
    }

private:
    // 进行玩家匹配
    void handle_match(match_queue<uint64_t>& mq)
    {
        while (true) 
        {
            // 1. 判断队列中的玩家人数是否大于2，小于2则阻塞等待
            while (mq.size() < 2) {
                mq.wait();
            }
            // 2. 如果人数大于等于2人，则出队两个玩家进行匹配
            uint64_t uid1, uid2;
            bool ret = mq.pop(uid1);
            if (ret == false) {
                continue;
            }
            ret = mq.pop(uid2);
            if (ret == false) {
                this->add(uid1); // 注意：需要将第玩家1重新添加到队列中
                continue;
            }
            // 3. 校验两个玩家是否在线，如果有人掉线了，则要把另一个玩家重新添加到队列中
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if (conn1.get() == nullptr) {
                this->add(uid2); // 玩家1掉线，需要将玩家2重新添加到队列中
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if (conn2.get() == nullptr) {
                this->add(uid1); // 玩家2掉线，需要将玩家1重新添加到队列中
                continue;
            }
            // 4. 为两个玩家创建房间，并将玩家加入房间
            room_ptr rp = _rm->create_room(uid1, uid2);
            if (rp.get() == nullptr) { // 由于玩家掉线导致创建房间
                this->add(uid1); // 将玩家1和玩家2重新添加到队列中
                this->add(uid2);
                continue;
            }
            // 5. 对两个玩家进行匹配成功的相应
            Json::Value response;
            response["optype"] = "match_success";
            response["result"] = true;
            std::string body;
            json_util::serialize(response, body);
            conn1->send(body);
            conn2->send(body);
        }
    }
    void th_normal_entry() { handle_match(_q_normal); }
    void th_high_entry() { handle_match(_q_high); }
    void th_super_entry() { handle_match(_q_super); }

private:
    match_queue<uint64_t> _q_normal; // 普通玩家匹配队列
    match_queue<uint64_t> _q_high;   // 高手玩家匹配队列
    match_queue<uint64_t> _q_super;  // 大神玩家匹配队列
    std::thread _th_normal; // 普通玩家匹配队列处理线程
    std::thread _th_high;   // 高手玩家匹配队列处理线程
    std::thread _th_super;  // 大神玩家匹配队列处理线程
    room_manager* _rm;      // 房间管理模块句柄
    user_table* _ut;        // 数据库管理模块用户表句柄
    online_manager* _om;    // 在线用户管理模块句柄
};